28. 农产品交易可视化性分析

⭐ 本章学习目标

  • 掌握农产品电商数据的探索性数据分析(EDA)方法
  • 学会使用箱线图、直方图、饼图等进行数据可视化
  • 理解缺失值检测、异常值识别等数据质量检查技术
  • 掌握时间特征提取与周末效应分析
  • 学会价格-销量关系分析与产品共现分析

⭐ 农产品电商的数据价值

农产品电子商务的快速发展产生了海量交易数据,通过可视化分析可以:

  • 识别季节性需求和价格波动模式
  • 发现区域消费偏好差异
  • 优化供应链和库存管理
  • 制定精准营销策略

⭐ 探索性数据分析(EDA)的四大原则

EDA 由统计学家 John Tukey 于 1977 年提出,核心思想是在正式建模前理解数据特征:

原则 说明
怀疑精神 对数据和假设保持质疑
可视化优先 图形比数字更直观
计算支撑 用统计量验证视觉发现
迭代探索 分析是循环往复的过程

⭐ 农产品数据的特殊性

农产品数据与一般商品数据相比,具有四大显著特征:

  • 季节性:生产受自然周期影响,供给随季节波动
  • 地域性:不同地区消费习惯差异大
  • 易腐性:保鲜期短,库存管理至关重要
  • 价格波动:受供需关系和政策影响明显

⭐ 平台任务解答代码

以下代码与教学平台任务要求完全一致:

Listing 1
# 注:processed_data.csv数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
# 导入必要的数据分析和可视化库
import pandas as pd
import numpy as np  # 导入NumPy数值计算库
import matplotlib.pyplot as plt  # 导入Matplotlib绑图库
import seaborn as sns  # 导入Seaborn可视化库
import warnings  # 导入warnings模块用于控制警告输出
warnings.filterwarnings('ignore')  # 忽略运行时警告信息以保持输出整洁
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体作为默认字体以支持中文
plt.rcParams['axes.unicode_minus'] = False  # 正确显示负号

# 读取处理好的数据集
data = pd.read_csv('processed_data.csv')

# 查看数据集的基本情况
print(data.head())  # 显示数据集前几行
print("查看数据集的基本信息:")  # 输出查看数据集的基本信息
print(data.info())  # 显示数据集的基本信息,包括列名、数据类型和非空值数量
print("查看数据集有无缺失值:")  # 输出查看数据集有无缺失值
print(data.isnull().sum())  # 统计每列的缺失值数量
print("查看数据集有无重复值:")  # 输出查看数据集有无重复值
print(data.duplicated().sum())  # 统计重复行的数量

# 创建特征映射字典,将英文列名映射为中文名称,用于图表标题
feature_map = {
    'price': '价格',  # 映射"price"→"价格"
    'quantity': '数量',  # 映射"quantity"→"数量"
    'customer_age': '顾客年龄',  # 映射"customer_age"→"顾客年龄"
    'return_flag': '是否返单',  # 映射"return_flag"→"是否返单"
    'sales_amount': '销售额'  # 映射"sales_amount"→"销售额"
}  # 数据结构定义结束

# 创建箱型图展示各特征的分布情况
plt.figure(figsize=(20,10))  # 设置图形大小
for i, feature in enumerate(feature_map.keys()):  # 循环遍历
    plt.subplot(2,3, i+1)  # 创建2行3列的子图布局
    plt.title(f'{feature_map[feature]}的箱型图')  # 设置子图标题
    plt.boxplot(data[feature])  # 绑制箱线图
    plt.xlabel(f'{feature_map[feature]}')  # 设置x轴标签
    plt.ylabel('频率')  # 设置y轴标签
plt.savefig("特征箱型图.png")  # 保存图形

# 创建一个字典来映射产品名称到正确的分类
category_mapping = {
    '安溪铁观音': '茶叶',  # 映射"安溪铁观音"→"茶叶"
    '武夷岩茶': '茶叶',  # 映射"武夷岩茶"→"茶叶"
    '福州茉莉花': '茶叶',  # 映射"福州茉莉花"→"茶叶"
    '古田银耳': '食用菌',  # 映射"古田银耳"→"食用菌"
    '建宁莲子': '中药材',  # 映射"建宁莲子"→"中药材"
    '琯溪蜜柚': '水果',  # 映射"琯溪蜜柚"→"水果"
    '宁德大黄鱼': '水产品'  # 映射"宁德大黄鱼"→"水产品"
}  # 数据结构定义结束

# 应用映射来更新category列,将产品名称转换为产品类别
data['category'] = data['product_name'].map(category_mapping).fillna(data['category'])

# 2.可视化展示 - 计算并转置描述性统计数据(结果未打印,仅计算)
data.describe().T

# 2.1 顾客信息可视化
plt.figure(figsize=(20,10))  # 设置图形大小

# 顾客年龄分布直方图
plt.subplot(2,2,1)
sns.distplot(data['customer_age'], bins=30, kde=True, color='blue')  # 绘制带核密度估计的直方图
plt.title('顾客年龄分布', fontsize=16)  # 设置标题
plt.xlabel('顾客年龄', fontsize=12)  # 设置x轴标签
plt.ylabel('频率', fontsize=12)  # 设置y轴标签

# 顾客年龄组别分布柱状图
plt.subplot(2,2,2)
order = ['18-25', '26-35', '36-45', '46-55', '56+']  # 自定义年龄组顺序
sns.countplot(data=data, x='age_group', color='orange', order=order)  # 按指定顺序绘制柱状图
plt.title('顾客年龄组别分布', fontsize=16)  # 设置标题
plt.xlabel('顾客年龄组别', fontsize=12)  # 设置x轴标签
plt.ylabel('频率', fontsize=12)  # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')

# 顾客性别分布柱状图
plt.subplot(2,2,3)
sns.countplot(data=data, x='customer_gender', color='green')  # 绘制性别分布柱状图
plt.title('顾客性别分布', fontsize=16)  # 设置标题
plt.xlabel('顾客性别', fontsize=12)  # 设置x轴标签
plt.ylabel('频率', fontsize=12)  # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')

# 顾客性别比例饼图
plt.subplot(2,2,4)
plt.pie(data['customer_gender'].value_counts(),  # 绑制饼图
        labels=data['customer_gender'].value_counts().index,  # 统计各个值的出现频次
        autopct='%1.1f%%', colors=['green', 'orange'])  # 绘制饼图并显示百分比
plt.title('顾客性别比例', fontsize=16)  # 设置标题
plt.axis('equal')  # 确保饼图是圆形
plt.savefig("顾客信息.png")  # 保存图形
#1、从顾客年龄组别的分布可以了解到,26-55岁间的顾客最多。
#2、从顾客性别的分布可以了解到,男性顾客比女性顾客多,男性占比60.4%,女性占比39.6%。

# 区域分布可视化
plt.figure(figsize=(20,5))  # 设置图形大小
sns.countplot(x='region', data=data, palette='Set2')  # 绑制计数柱状图
plt.title('区域分布', fontsize=20)  # 设置标题
plt.xlabel('区域', fontsize=16)  # 设置x轴标签
plt.ylabel('数量', fontsize=16)  # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')
plt.savefig("区域分布.png")  # 保存图形

# 2.2 商品信息可视化
plt.figure(figsize=(20,5))  # 设置图形大小

# 产品名称分布柱状图
plt.subplot(1, 2, 1)
sns.countplot(x='product_name', data=data, palette='Set2')  # 绘制产品名称分布柱状图
plt.xlabel('产品名称', fontsize=16)  # 设置x轴标签
plt.ylabel('数量', fontsize=16)  # 设置y轴标签
plt.title('产品分布', fontsize=20)  # 设置标题
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')

# 产品类型分布柱状图
plt.subplot(1, 2, 2)
sns.countplot(x='category', data=data, palette='Set2')  # 绘制产品类型分布柱状图
plt.xlabel('产品类型', fontsize=16)  # 设置x轴标签
plt.ylabel('数量', fontsize=16)  # 设置y轴标签
plt.title('产品类型分布', fontsize=20)  # 设置标题
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')
plt.savefig("商品信息.png")  # 保存图形
#总体而言,茶叶是销量最多的产品类型,远超其他产品类型。
#具体产品分布上,销量最多的产品是古田银耳,共1474单;其次是安溪铁观音,共1461单;第三名是宁德大黄鱼,共1438单。

# 销售相关指标分布可视化
plt.figure(figsize=(20,6))  # 设置图形大小

# 价格分布直方图
plt.subplot(1, 3, 1)
sns.distplot(data['price'], kde=True, color='blue')  # 绘制价格分布直方图
plt.title('价格分布', fontsize=16)  # 设置标题
plt.xlabel('价格', fontsize=12)  # 设置x轴标签
plt.ylabel('频率', fontsize=12)  # 设置y轴标签

# 数量分布直方图
plt.subplot(1, 3, 2)
sns.distplot(data['quantity'], kde=True, color='green')  # 绘制数量分布直方图
plt.title('数量分布', fontsize=16)  # 设置标题
plt.xlabel('数量', fontsize=12)  # 设置x轴标签
plt.ylabel('频率', fontsize=12)  # 设置y轴标签

# 销售额分布直方图
plt.subplot(1, 3, 3)
sns.distplot(data['sales_amount'], kde=True, color='orange')  # 绘制销售额分布直方图
plt.title('销售额分布', fontsize=16)  # 设置标题
plt.xlabel('销售额', fontsize=12)  # 设置x轴标签
plt.ylabel('频率', fontsize=12)  # 设置y轴标签
plt.savefig("销售.png")  # 保存图形

# 2.3 平台及优惠信息可视化
plt.figure(figsize=(20,12))  # 设置图形大小

# 平台分布柱状图
plt.subplot(2, 3, 1)
sns.countplot(x='channel', data=data, palette='Set2')  # 绘制平台分布柱状图
plt.title('平台分布', fontsize=16)  # 设置标题
plt.xlabel('平台', fontsize=14)  # 设置x轴标签
plt.ylabel('频数', fontsize=14)  # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')

# 平台比例饼图
plt.subplot(2, 3 ,2)
plt.pie(data['channel'].value_counts(),  # 绑制饼图
        labels=data['channel'].value_counts().index,  # 统计各个值的出现频次
        autopct='%1.1f%%')  # 绘制平台比例饼图
plt.title('平台比例', fontsize=16)  # 设置标题

# 优惠情况分布柱状图
plt.subplot(2,3, 3)
sns.countplot(x='promotion', data=data, palette='Set2')  # 绘制优惠情况分布柱状图
plt.title('优惠情况分布', fontsize=16)  # 设置标题
plt.xlabel('优惠情况', fontsize=14)  # 设置x轴标签
plt.ylabel('频数', fontsize=14)  # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')

# 优惠情况比例饼图
plt.subplot(2, 3, 4)
plt.pie(data['promotion'].value_counts(),  # 绑制饼图
        labels=data['promotion'].value_counts().index,  # 统计各个值的出现频次
        autopct='%1.1f%%')  # 绘制优惠情况比例饼图
plt.title('优惠情况比例', fontsize=16)  # 设置标题

# 是否返单分布柱状图
plt.subplot(2, 3, 5)
sns.countplot(x='return_flag', data=data, palette='Set2')  # 绘制是否返单分布柱状图
plt.title('是否返单分布', fontsize=16)  # 设置标题
plt.xlabel('是否返单', fontsize=14)  # 设置x轴标签
plt.ylabel('频数', fontsize=14)  # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
    plt.gca().annotate(format(p.get_height(), '.0f'),  # 获取当前坐标轴并在柱状图顶部标注数值
                       (p.get_x() + p.get_width() / 2., p.get_height()),  # 设置标注位置为柱子顶部中心
                       # 设置标注文本的水平对齐、垂直偏移等显示参数
                       ha='center', va='center', xytext=(0, 5), textcoords='offset points')

# 是否返单比例饼图
plt.subplot(2, 3, 6)
plt.pie(data['return_flag'].value_counts(),  # 绑制饼图
        labels=data['return_flag'].value_counts().index,  # 统计各个值的出现频次
        autopct='%1.1f%%')  # 绘制是否返单比例饼图
plt.title('是否返单比例', fontsize=16)  # 设置标题
plt.savefig("平台及优惠信息.png")  # 保存图形
#从平台的分布上可以了解到,淘宝订单数量是最多的,占比29.6%;京东平台订单数量第二,共2504单,占比25%;第三是拼多多平台;自由平台的订单数量最少。
#从优惠情况可以了解到,没有优惠的订单占比50.3%,有优惠的订单占比49.7%。其中,满减订单占比29.4%,折扣订单占比20.3%。
#是否返单的情况可以了解到,有返单的订单占比4.8%,没有返单的订单占比95.2%。

第一部分:数据准备与探索

⭐ 数据读取与初步检查

数据预处理是分析的第一步,通过查看数据结构和基本统计信息来了解数据质量:

  • shape:返回数据维度(行数, 列数)
  • info():显示每列的数据类型和非空值数量
  • describe().T:转置描述统计表,更易阅读
  • isnull().sum():统计每列缺失值数量

⭐ 数据读取代码详解

Listing 2
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 读取数据并输出基本信息
data = pd.read_csv('processed_data.csv')
print('数据形状:', data.shape)
print('\n数据前5行:')
print(data.head())
print('\n数据信息:')
print(data.info())
print('\n描述统计:')
print(data.describe().T)

⭐ 数据类型检查要点

在初步检查中,需要关注三类数据类型:

数据类型 示例字段 说明
数值型 (int/float) 年龄、金额、数量 可直接参与计算
字符型 (object) 产品名称、类别 需要编码或分组分析
日期型 (datetime) 下单日期 需要格式转换后提取特征

⭐ 中文显示配置说明

在 Matplotlib 中显示中文需要两步配置:

  • plt.rcParams['font.sans-serif'] = ['SimHei']:设置黑体为默认字体
  • plt.rcParams['axes.unicode_minus'] = False:修复负号显示问题

⭐ 产品分类映射的意义

产品分类是电商数据分析的基础,农产品通常采用混合分类策略

  • 一级分类:茶叶、水果、水产品等大类
  • 二级分类:具体品种(铁观音、武夷岩茶)
  • 三级分类:产地、等级等细分

⭐ 产品分类映射代码

Listing 3
# 产品名称到分类的映射字典
category_mapping = {
    '安溪铁观音': '茶叶',
    '武夷岩茶': '茶叶',
    '福州茉莉花': '茶叶',
    '古田银耳': '食用菌',
    '建宁莲子': '中药材',
    '琯溪蜜柚': '水果',
    '宁德大黄鱼': '水产品'
}

# map()根据字典映射,fillna()保留未匹配的值
data['category'] = data['product_name'].map(
    category_mapping
).fillna(data['category'])

# 验证映射结果
print('分类映射结果:')
print(data[['product_name', 'category']].drop_duplicates())
print('\n分类统计:')
print(data['category'].value_counts())

map() 函数的核心机制

  • map(dict):根据字典进行键值映射
  • fillna():处理未匹配的值(保留原分类)
  • for 循环映射高效 100 倍以上
  • drop_duplicates():去重查看唯一映射关系

第二部分:数据质量检查

⭐ 缺失值的三种类型

根据 Rubin (1976) 的分类,缺失值分为三种:

类型 含义 处理方法
MCAR 完全随机缺失,与任何变量无关 直接删除或插补
MAR 缺失与观测变量相关 多重插补
MNAR 缺失与未观测变量相关 建模机制

⭐ 缺失值检查与可视化代码

Listing 4
# 缺失值统计
missing_counts = data.isnull().sum()
missing_pct = (missing_counts / len(data)) * 100
missing_df = pd.DataFrame({
    '缺失数': missing_counts,
    '缺失比例': missing_pct
})
print(missing_df)

# 缺失值可视化
plt.figure(figsize=(10, 6))
missing_df['缺失比例'].plot(kind='bar', color='steelblue', alpha=0.7)
plt.title('各字段缺失值比例', fontsize=14)
plt.xlabel('字段名', fontsize=12)
plt.ylabel('缺失比例(%)', fontsize=12)
plt.xticks(rotation=45)
plt.axhline(y=5, color='red', linestyle='--', label='5%阈值')
plt.legend()
plt.tight_layout()
plt.show()

⭐ 重复值检查与数据清洗

Listing 5
# 重复值检查
print(f'重复值数量: {data.duplicated().sum()}')
print(f'重复值比例: {data.duplicated().sum()/len(data)*100:.2f}%')

# 数据清洗:删除重复行
data_clean = data.drop_duplicates()
print(f'\n清洗后数据形状: {data_clean.shape}')
print(f'数据保留率: {len(data_clean)/len(data)*100:.2f}%')

⭐ 异常值的识别方法

异常值 (Outlier) 指显著偏离其他观测值的数据点:

方法类别 具体方法 适用场景
基于统计量 3σ 原则、IQR 方法 正态/近似正态分布
基于可视化 箱线图、散点图 快速直观判断
基于模型 DBSCAN、孤立森林 高维复杂数据

⭐ IQR 异常值检测原理

IQR 方法的判定规则:

  • \(Q_1\):第一四分位数(25% 分位)
  • \(Q_3\):第三四分位数(75% 分位)
  • \(IQR = Q_3 - Q_1\):四分位距
  • 下界\(Q_1 - 1.5 \times IQR\)
  • 上界\(Q_3 + 1.5 \times IQR\)

超出上下界的数据点即为异常值。

⭐ 异常值检测代码

Listing 6
# 选择数值型字段
numeric_cols = data_clean.select_dtypes(include=[np.number]).columns

# 绘制箱线图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes = axes.flatten()
for i, col in enumerate(numeric_cols[:4]):
    data_clean.boxplot(column=col, ax=axes[i])
    axes[i].set_title(f'{col} - 箱线图', fontsize=12)
    axes[i].grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()

# IQR 异常值检测函数
def detect_outliers_iqr(series):
    Q1 = series.quantile(0.25)
    Q3 = series.quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    return ((series < lower) | (series > upper)).sum()

# 输出异常值统计
print('\n异常值统计(IQR方法):')
for col in numeric_cols:
    outlier_count = detect_outliers_iqr(data_clean[col].dropna())
    print(f'{col}: {outlier_count}个异常值')

第三部分:数据分布分析

⭐ 描述数据分布的三个维度

维度 指标 含义
集中趋势 均值、中位数、众数 数据的中心位置
离散程度 方差、标准差、极差 数据偏离中心的程度
分布形状 偏度、峰度 对称性和尖锐程度

⭐ 偏度与峰度的解读

  • 偏度 (Skewness):衡量分布的对称性
    • 偏度 ≈ 0:接近对称分布
    • 偏度 > 0:右偏(存在高值异常)
    • 偏度 < 0:左偏(存在低值异常)
  • 峰度 (Kurtosis):衡量分布的尖锐程度
    • 峰度 > 0:尖峰分布
    • 峰度 < 0:平峰分布

⭐ 销售额分布分析代码

Listing 7
from scipy.stats import skew, kurtosis

sales = data_clean['sales_amount']

# 绘制分布图
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 直方图(含均值和中位数参考线)
axes[0].hist(sales, bins=50, color='steelblue', alpha=0.7,
             edgecolor='black')
axes[0].axvline(sales.mean(), color='red', linestyle='--',
                linewidth=2, label=f'均值={sales.mean():.2f}')
axes[0].axvline(sales.median(), color='green', linestyle='--',
                linewidth=2, label=f'中位数={sales.median():.2f}')
axes[0].set_title('销售额分布直方图', fontsize=14)
axes[0].set_xlabel('销售额(元)', fontsize=12)
axes[0].set_ylabel('频数', fontsize=12)
axes[0].legend(fontsize=10)

# 箱线图
axes[1].boxplot(sales, vert=True)
axes[1].set_title('销售额箱线图', fontsize=14)
axes[1].set_ylabel('销售额(元)', fontsize=12)

plt.tight_layout()
plt.show()

# 输出分布统计量
print(f'均值: {sales.mean():.2f}')
print(f'中位数: {sales.median():.2f}')
print(f'标准差: {sales.std():.2f}')
print(f'偏度: {skew(sales):.4f}')
print(f'峰度: {kurtosis(sales):.4f}')

⭐ 品类分布分析代码

Listing 8
# 按品类聚合统计
category_sales = data_clean.groupby('category')['sales_amount'].agg([
    'sum', 'count', 'mean'
])
category_sales.columns = ['总销售额', '订单数', '平均单价']
category_sales = category_sales.sort_values('总销售额', ascending=False)
print('\n品类销售统计:')
print(category_sales)

# 可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 总销售额柱状图
category_sales['总销售额'].plot(kind='bar', ax=axes[0],
                               color='steelblue', alpha=0.7)
axes[0].set_title('各品类总销售额', fontsize=14)
axes[0].set_xlabel('产品类别', fontsize=12)
axes[0].set_ylabel('销售额(元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=45)

# 订单数饼图
axes[1].pie(category_sales['订单数'],
            labels=category_sales.index,
            autopct='%1.1f%%', startangle=90,
            colors=sns.color_palette('Set3'))
axes[1].set_title('各品类订单数占比', fontsize=14)

plt.tight_layout()
plt.show()

⭐ 品类分析的关键发现

根据可视化结果,可以识别出:

  • 核心品类:茶叶是销量最高的产品类型,远超其他品类
  • Top 3 产品:古田银耳(1474单) > 安溪铁观音(1461单) > 宁德大黄鱼(1438单)
  • 长尾品类:部分品类订单量较少,需考虑是否调整运营策略

第四部分:时间序列分析

⭐ 时间序列数据的四大特征

特征 说明 示例
趋势性 长期上升或下降趋势 年销售额逐年增长
季节性 固定周期的波动 春节前后销量高峰
周期性 不固定周期的波动 经济周期影响消费
随机性 不可预测的噪声 突发事件造成波动

⭐ 时间特征提取代码

Listing 9
# 确保日期格式正确
data_clean['下单日期'] = pd.to_datetime(data_clean['order_date'])

# 提取时间特征
data_clean['年'] = data_clean['下单日期'].dt.year
data_clean['月'] = data_clean['下单日期'].dt.month
data_clean['日'] = data_clean['下单日期'].dt.day
data_clean['星期'] = data_clean['下单日期'].dt.dayofweek
data_clean['小时'] = data_clean['下单日期'].dt.hour

# 按月统计
monthly_sales = data_clean.groupby('月')['sales_amount'].agg([
    'sum', 'count'
])
monthly_sales.columns = ['月销售额', '月订单数']
print('月度销售统计:')
print(monthly_sales)

⭐ 月度趋势可视化

Listing 10
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 月销售额折线图
monthly_sales['月销售额'].plot(ax=axes[0], marker='o', linewidth=2)
axes[0].set_title('月销售额趋势', fontsize=14)
axes[0].set_xlabel('月份', fontsize=12)
axes[0].set_ylabel('销售额(元)', fontsize=12)
axes[0].grid(True, alpha=0.3)

# 月订单数柱状图
monthly_sales['月订单数'].plot(kind='bar', ax=axes[1],
                               color='coral', alpha=0.7)
axes[1].set_title('月订单数分布', fontsize=14)
axes[1].set_xlabel('月份', fontsize=12)
axes[1].set_ylabel('订单数', fontsize=12)

plt.tight_layout()
plt.show()

⭐ 周末效应分析

分析工作日与周末的消费差异,为营销和物流策略提供依据:

Listing 11
# 标记周末(5=周六, 6=周日)
data_clean['是否周末'] = data_clean['星期'].isin([5, 6])

# 周末 vs 工作日对比
weekend_comparison = data_clean.groupby('是否周末')[
    'sales_amount'
].agg(['mean', 'sum', 'count'])
weekend_comparison.index = ['工作日', '周末']
print('周末效应分析:')
print(weekend_comparison)

⭐ 周末效应可视化

Listing 12
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 平均销售额对比
weekend_comparison['mean'].plot(kind='bar', ax=axes[0],
    color=['skyblue', 'orange'], alpha=0.7)
axes[0].set_title('平均销售额对比', fontsize=14)
axes[0].set_ylabel('平均销售额(元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=0)

# 总销售额对比
weekend_comparison['sum'].plot(kind='bar', ax=axes[1],
    color=['skyblue', 'orange'], alpha=0.7)
axes[1].set_title('总销售额对比', fontsize=14)
axes[1].set_ylabel('总销售额(元)', fontsize=12)
axes[1].tick_params(axis='x', rotation=0)

plt.tight_layout()
plt.show()

第五部分:多变量关系探索

⭐ 价格弹性的经济学含义

价格弹性 (Price Elasticity) 衡量需求量对价格变化的敏感度:

\[E_p = \frac{\%\Delta Q}{\%\Delta P} = \frac{\partial Q}{\partial P} \times \frac{P}{Q}\]

  • \(|E_p| > 1\):富有弹性(奢侈品)
  • \(|E_p| < 1\):缺乏弹性(必需品)
  • \(|E_p| = 1\):单位弹性

⭐ 价格与销量关系分析代码

Listing 13
# 按产品统计价格与销量
product_stats = data_clean.groupby('product_name').agg({
    'sales_amount': 'sum',
    'quantity': 'sum',
    'unit_price': 'mean'
})
product_stats['单价'] = (product_stats['sales_amount']
                         / product_stats['quantity'])

# 计算相关系数
corr_coef = product_stats[['单价', 'quantity']].corr().iloc[0, 1]
print(f'价格与销量的相关系数: {corr_coef:.4f}')

⭐ 价格-销量散点图与热力图

Listing 14
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 散点图
axes[0].scatter(product_stats['单价'],
                product_stats['quantity'], s=100, alpha=0.6)
axes[0].set_title(f'价格-销量散点图 (r={corr_coef:.4f})', fontsize=14)
axes[0].set_xlabel('单价(元)', fontsize=12)
axes[0].set_ylabel('销量', fontsize=12)
# 添加产品标签
for idx, row in product_stats.iterrows():
    axes[0].annotate(idx, (row['单价'], row['quantity']), fontsize=8)

# 热力图
sns.heatmap(product_stats[['单价', 'quantity']].corr(),
            annot=True, cmap='coolwarm', center=0, ax=axes[1],
            cbar_kws={'label': '相关系数'})
axes[1].set_title('相关性热力图', fontsize=14)

plt.tight_layout()
plt.show()

⭐ 产品共现分析的意义

产品共现分析可以发现哪些产品经常被一起购买:

  • 捆绑销售:将高共现产品组合销售
  • 推荐系统:购买 A 产品的用户可能也想买 B
  • 货架摆放:将关联产品相邻放置

⭐ 产品共现分析代码

Listing 15
from itertools import combinations

# 获取每个订单的产品列表
order_products = data_clean.groupby('order_id')[
    'product_name'
].apply(list)

# 统计产品对共现次数
co_occurrence = {}
for products in order_products:
    for combo in combinations(sorted(products), 2):
        co_occurrence[combo] = co_occurrence.get(combo, 0) + 1

# 转换为 DataFrame 并排序
co_df = pd.DataFrame(list(co_occurrence.items()),
                     columns=['产品对', '共现次数'])
co_df = co_df.sort_values('共现次数', ascending=False).head(10)
print('Top 10产品组合:')
print(co_df)

⭐ 产品共现可视化

Listing 16
plt.figure(figsize=(12, 6))
plt.barh(range(len(co_df)), co_df['共现次数'],
         color='steelblue', alpha=0.7)
plt.yticks(range(len(co_df)),
           [f'{p1} + {p2}' for p1, p2 in co_df['产品对']])
plt.xlabel('共现次数', fontsize=12)
plt.title('产品共现分析(Top 10)', fontsize=14)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()

总结

⭐ 本章核心知识回顾

分析环节 核心方法 关键工具
数据准备 分类映射、类型转换 map(), pd.to_datetime()
质量检查 缺失值、异常值检测 isnull(), IQR 方法
分布分析 偏度、峰度、直方图 scipy.stats, hist()
时间分析 月度趋势、周末效应 dt 属性、groupby()
多变量 相关分析、共现分析 corr(), combinations

⭐ 关键业务发现

  • 顾客画像:26-55 岁为主力消费群体,男性占比 60.4%
  • 产品格局:茶叶是销量最高的品类,古田银耳为 Top 1 单品
  • 渠道分布:淘宝订单最多(29.6%),京东第二(25%)
  • 优惠策略:约 50% 订单享受优惠,满减占比高于折扣
  • 退货率低:仅 4.8% 的订单存在返单